package net.quanter.shield.common.dto.result.wapper;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.ToString;
import net.quanter.shield.common.dto.result.ResultDTO;
import net.quanter.shield.common.dto.result.page.ListFullPageResultDTO;
import org.jetbrains.annotations.NotNull;

/**
 * List类型通用返回DTO
 *
 * @author 王老实
 * @since 1.3.12.RELEASE
 */
@Schema(description = "List类型通用返回DTO")
@ToString
public class ResultDTOListWapper<T extends Serializable> implements List<T> {

    @Getter
    private final ResultDTO<List<T>> resultDTO;

    public ResultDTOListWapper(ResultDTO<List<T>> resultDTO) {
        this.resultDTO = resultDTO == null ? emptyList() : resultDTO;
    }

    public static <T extends Serializable> ResultDTO<List<T>> emptyList() {
        return new ResultDTO<>(new ArrayList<>());
    }

    public static <T extends Serializable> ResultDTOListWapper<T> emptyListWapper() {
        return new ResultDTOListWapper(emptyList());
    }

    public <N extends Serializable> ResultDTOListWapper<N> convert(Function<T, N> action) {
        ResultDTO<List<N>> newResultDTO = new ResultDTO(resultDTO.isSuccess(),resultDTO.getMessage(),resultDTO.getCode(),null);
        List<N> dataList = resultDTO.get()
                .map(x -> x.stream().map(action).collect(Collectors.toList()))
                .orElse(new ArrayList<>());
        newResultDTO.setData(dataList);
        return new ResultDTOListWapper(newResultDTO);
    }

    public List<T> getData() {
        return resultDTO.getData();
    }

    @Override
    public boolean add(T t) {
        return resultDTO.get().map(x -> x.add(t)).orElse(false);
    }

    @Override
    public boolean remove(Object o) {
        return resultDTO.get().map(x -> x.remove(o)).orElse(false);
    }

    @Override
    public boolean containsAll(@NotNull Collection<?> c) {
        return resultDTO.get().map(x -> x.containsAll(c)).orElse(false);
    }

    @Override
    public boolean addAll(@NotNull Collection<? extends T> c) {
        return resultDTO.get().map(x -> x.addAll(c)).orElse(false);
    }

    @Override
    public boolean addAll(int index, @NotNull Collection<? extends T> c) {
        return resultDTO.get().map(x -> x.addAll(index, c)).orElse(false);
    }

    @Override
    public boolean removeAll(@NotNull Collection<?> c) {
        return resultDTO.get().map(x -> x.removeAll(c)).orElse(false);
    }

    @Override
    public boolean removeIf(Predicate<? super T> filter) {
        return resultDTO.get().map(x -> x.removeIf(filter)).orElse(false);
    }

    @Override
    public boolean retainAll(@NotNull Collection<?> c) {
        return resultDTO.get().map(x -> x.retainAll(c)).orElse(false);
    }

    @Override
    public void replaceAll(UnaryOperator<T> operator) {
        resultDTO.get().ifPresent(x -> x.replaceAll(operator));
    }

    @Override
    public void sort(Comparator<? super T> c) {
        resultDTO.get().ifPresent(x -> x.sort(c));
    }

    @Override
    public boolean isEmpty() {
        return resultDTO.get().map(List::isEmpty).orElse(true);
    }

    @Override
    public boolean contains(Object o) {
        return resultDTO.get().map(x -> x.contains(o)).orElse(false);
    }

    private final List<T> emptyList = List.of();

    @Override
    public @NotNull Iterator<T> iterator() {
        /**
         * list = resultDTO.get();
         * if(list!=null){
         *     return list.iterator()
         * }else{
         *     return emptyList.iterator()
         * }
         */
        return resultDTO.get().map(List::iterator).orElse(emptyList.iterator());
    }

    @Override
    public void forEach(Consumer<? super T> action) {
        resultDTO.get().ifPresent(x -> x.forEach(action));
    }

    @Override
    public Object[] toArray() {
        return resultDTO.get().map(List::toArray).orElse(new Object[] {});
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return resultDTO.get().map(x -> x.toArray(a)).orElse(a);
    }

    @Override
    public int size() {
        return resultDTO.get().map(List::size).orElse(0);
    }

    @Override
    public void clear() {
        resultDTO.get().ifPresent(List::clear);
    }

    @Override
    public T get(int index) {
        return resultDTO.get().map(x -> x.get(index)).orElse(null);
    }

    @Override
    public T set(int index, T element) {
        return resultDTO.get().map(x -> x.set(index, element)).orElse(null);
    }

    @Override
    public void add(int index, T element) {
        resultDTO.get().ifPresent(x -> x.add(index, element));
    }

    @Override
    public T remove(int index) {
        return resultDTO.get().map(x -> x.remove(index)).orElse(null);
    }

    @Override
    public int indexOf(Object o) {
        return resultDTO.get().map(x -> x.indexOf(o)).orElse(-1);
    }

    @Override
    public int lastIndexOf(Object o) {
        return resultDTO.get().map(x -> x.lastIndexOf(o)).orElse(-1);
    }

    @Override
    public @NotNull ListIterator<T> listIterator() {
        return resultDTO.get().map(List::listIterator).orElse(emptyList.listIterator());
    }

    @Override
    public @NotNull ListIterator<T> listIterator(int index) {
        return resultDTO.get().map(x -> x.listIterator(index)).orElse(emptyList.listIterator());
    }

    @Override
    public @NotNull List<T> subList(int fromIndex, int toIndex) {
        return resultDTO.get().map(x -> x.subList(fromIndex, toIndex)).orElse(emptyList);
    }

    @Override
    public Spliterator<T> spliterator() {
        return resultDTO.get().map(List::spliterator).orElse(emptyList.spliterator());
    }

    @Override
    public Stream<T> stream() {
        return resultDTO.get().map(List::stream).orElse(emptyList.stream());
    }

    @Override
    public Stream<T> parallelStream() {
        return resultDTO.get().map(List::parallelStream).orElse(emptyList.parallelStream());
    }
}
